package web.filter;


import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import web.entity.LoginUser;
import web.entity.User;
import web.service.PermissionSerive;
import web.service.UserService;
import web.utils.JwtUtil;
import web.utils.RedisCache;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * 每个请求过来后,解析token的过滤器
 * 为何是继承`OncePerRequestFilter`,而不是实现`Filter`呢?
 * 因为Filter本身存在问题.由于版本的不同,可能会导致过滤器被调用多次,
 * 所以采用spring提供的OncePerRequestFilter,保证只调用一次.
 * 想要对于一个通过验证的请求进行放行:
 * 执行 filterChain.doFilter(request,response); 即可
 */
@Component
public class CheckTokenFilter extends OncePerRequestFilter {
    @Value("${redis_conf.login-prefix}")
    String redisPrefix;
    @Autowired
    RedisCache redisCache;

    @Autowired
    UserService userService;

    @Autowired
    PermissionSerive permissionSerive;



    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取token
        String ps_token = request.getHeader("PStoken");
        if (!StringUtils.hasText(ps_token)){
            //放行 + return??
            //放行是因为:后续的过滤器会检测SecurityContextHolder的认证状态,这里直接放行就没有认证状态,自然会拦截
            //return是因为:本方法中的后续流程是验证token,既然没有token,也没有必要继续验证了
            //todo 为什么不直接return?
            //
            filterChain.doFilter(request,response);
            return;
        }
        //解析token获取id
        String userid = "";
        try {
            Claims claims = JwtUtil.parseJWT(ps_token);
            userid = claims.getSubject();
        } catch (Exception e) {
            e.printStackTrace();
            filterChain.doFilter(request,response);
            return;
//            throw new RuntimeException("非法token!");
        }
        //从reids中通过id获取用户信息
        String redisKey = redisPrefix + userid;
        LoginUser user = redisCache.getCacheObject(redisKey);
        if (Objects.isNull(user)){
            LoginUser user1 = attemptGetLoginUserInDB(userid);
            user = user1;
            if (Objects.isNull(user.getUser())){
                filterChain.doFilter(request,response);
                return;
            }
            //todo 容错

//            throw new RuntimeException("应该去数据库查询!");
        }
        //todo 没有权限信息
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities());
//                new UsernamePasswordAuthenticationToken(user,null,null);
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        System.out.println("执行checktokenFileter");
        filterChain.doFilter(request,response);
    }

    /**
     * 如果redis中没有数据,如果redis死机...
     * 从mysql中获取用户信息,封装成LoginUser对象
     * @param userid 用户id
     * @return LoginUser对象
     */
    protected LoginUser attemptGetLoginUserInDB(String userid){
        User user = userService.selectOne(userid);
        List<String> pers = permissionSerive.getPersByID(userid);
        LoginUser user1 = new LoginUser();
        if (!Objects.isNull(user)){
            user1.setUser(user);
            user1.setPermissions(pers);
        }
        return user1;
    }
}
